package com.easy.mongodb.starter.register;

import com.easy.mongodb.common.annotation.Intercepts;
import com.easy.mongodb.common.enums.ProcessIndexStrategyEnum;
import com.easy.mongodb.common.utils.LogUtils;
import com.easy.mongodb.common.utils.TypeUtils;
import com.easy.mongodb.core.biz.TableInfo;
import com.easy.mongodb.core.cache.BaseCache;
import com.easy.mongodb.core.cache.GlobalConfigCache;
import com.easy.mongodb.core.config.GlobalConfig;
import com.easy.mongodb.core.proxy.MongoMapperProxy;
import com.easy.mongodb.core.toolkit.TableInfoHelper;
import com.easy.mongodb.extension.context.Interceptor;
import com.easy.mongodb.extension.context.InterceptorChain;
import com.easy.mongodb.extension.context.InterceptorChainHolder;
import com.easy.mongodb.starter.config.EasyMongoDBConfigProperties;
import com.easy.mongodb.starter.factory.IndexStrategyFactory;
import com.easy.mongodb.starter.service.AutoProcessIndexService;
import com.mongodb.client.MongoClient;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * ProjectName: easy-mongodb
 * Description: 代理类
 * Author: vapeshop
 * Date: 2022/6/20 15:14:45
 * UpdateUser: vapeshop
 * UpdateDate: 2022/6/20 15:14:45
 * UpdateRemark: The modified content
 * Version: 1.0
 * <p>
 * Copyright © 2022 vapeshop Technologies Inc. All Rights Reserved
 **/
public class MapperFactoryBean<T> implements FactoryBean<T> {
    private Class<T> mapperInterface;

    @Autowired
    private MongoClient client;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private IndexStrategyFactory indexStrategyFactory;

    @Autowired
    private EasyMongoDBConfigProperties easyMongoDBConfigProperties;

    public MapperFactoryBean() {
    }

    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    @Override
    public T getObject() throws Exception {

        MongoMapperProxy<T> mongoMapperProxy = new MongoMapperProxy<>(this.mapperInterface);

        // 获取实体类
        Class<?> entityClass = TypeUtils.getInterfaceT(this.mapperInterface, 0);

        // 初始化缓存
        GlobalConfigCache.setGlobalConfig(this.easyMongoDBConfigProperties.getGlobalConfig());
        BaseCache.initCache(this.mapperInterface, entityClass, this.client);

        // 创建代理
        T t = (T) Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mongoMapperProxy);

        // 初始化拦截器链
        InterceptorChain interceptorChain = this.initInterceptorChain();

        // 异步处理索引创建/更新/数据迁移等
        GlobalConfig globalConfig = this.easyMongoDBConfigProperties.getGlobalConfig();
        if (!ProcessIndexStrategyEnum.MANUAL.equals(globalConfig.getProcessIndexMode())) {
            // 父子类型,仅针对父类型创建索引,子类型不创建索引
            TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
//            if (!entityInfo.isChild()) {
            AutoProcessIndexService autoProcessIndexService = this.indexStrategyFactory
                    .getByStrategyType(globalConfig.getProcessIndexMode().getStrategyType());
            autoProcessIndexService.processIndexAsync(entityClass, this.client);
//
//                // 将子文档索引激活为父文档索引
//                if (!DefaultChildClass.class.equals(entityInfo.getChildClass())) {
//                    EntityInfoHelper.getEntityInfo(entityInfo.getChildClass()).setCollectionName(entityInfo.getCollectionName());
//                }
//            }
        } else {
            LogUtils.info("===> manual index mode activated");
        }
        return interceptorChain.pluginAll(t);
    }

    @Override
    public Class<?> getObjectType() {
        return this.mapperInterface;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    private InterceptorChain initInterceptorChain() {
        InterceptorChainHolder interceptorChainHolder = InterceptorChainHolder.getInstance();
        InterceptorChain interceptorChain = interceptorChainHolder.getInterceptorChain();
        if (interceptorChain == null) {
            synchronized (this) {
                interceptorChainHolder.initInterceptorChain();
                Map<String, Object> beansWithAnnotation = this.applicationContext.getBeansWithAnnotation(Intercepts.class);
                beansWithAnnotation.forEach((key, val) -> {
                    if (val instanceof Interceptor) {
                        Interceptor interceptor = (Interceptor) val;
                        interceptorChainHolder.addInterceptor(interceptor);
                    }
                });
            }
        }
        return interceptorChainHolder.getInterceptorChain();
    }

}
